#!/bin/sh

# Run wpi.sh on plume-lib projects.
# For each project:
#  * clone it
#  * remove its annotations
#  * run WPI to infer annotations
#  * type-check the annotated version
#  * check that the output of type-checking is the same as the *.expected file in this directory
# Afterward, the inferred annotations can be found in a directory named /tmp/wpi-ajava-XXXXXX .
# The exact directory name is the last directory in the -Ajava= argument in file
# checker-framework/checker/build/wpi-plumelib-tests/PROJECTNAME/dljc-out/typecheck.out .

# This script is run by `./gradlew wpiPlumeLibTest` at the top level.

# wpi.sh may exit with non-zero status.
set +e

# set -o verbose
# set -o xtrace
export SHELLOPTS
# echo "SHELLOPTS=${SHELLOPTS}"

SCRIPT_DIR="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd -P)"
CHECKERFRAMEWORK="$(cd "$(dirname "$0")"/../../.. && pwd)"

# Do not use a subdirectory of $CHECKERFRAMEWORK because if a project has no
# `settings.gradle` file, Gradle will find one in $CHECKERFRAMEWORK.
TESTDIR=$(mktemp -d "${TMPDIR:-/tmp}"/wpi-plumelib-tests-"$(date +%Y%m%d-%H%M%S)"-XXX)

# Takes two arguments, an input file (produced by compilation) and an output file.
# Copies the input to the output, removing parts that might differ from run to run.
clean_compile_output() {
  in="$1"
  out="$2"

  cp -f "$in" "$out" || exit 1

  # Remove "Running ..." line
  sed -i '/^Running /d' "$out"

  # Remove comments starting with "#" and blank lines
  sed -i '/^#/d' "$out"
  sed -i '/^$/d' "$out"

  # Remove uninteresting output
  sed -i '/^warning: \[path\] bad path element /d' "$out"
  sed -i '/^warning: \[options\] bootstrap class path not set/d' "$out"
  sed -i '/^warning: \[options\] system modules path not set in conjunction with -source 11/d' "$out"

  # Remove warning count because it can differ between JDK 8 and later JDKs due to the bootstrap warning:
  sed -i '/^[0-9]* warning/d' "$out"

  # Remove directory names and line numbers
  sed -i 's/^[^ ]*\///' "$out"
  sed -i 's/:[0-9]+: /: /' "$out"
}

# Takes two arguments, the project name and the comma-separated list of checkers to run.
test_wpi_plume_lib() {
  project="$1"
  checkers="$2"

  rm -rf "$project"
  # Try twice in case of network lossage
  git clone -q --depth=1 "https://github.com/plume-lib/$project.git" || (sleep 60 && git clone -q --depth=1 "https://github.com/plume-lib/$project.git")

  cd "$project" || {
    echo "can't run: cd $project"
    exit 2
  }

  java -cp "$CHECKERFRAMEWORK/checker/dist/checker.jar" org.checkerframework.framework.stub.RemoveAnnotationsForInference . || exit 1
  # The project may not build after running RemoveAnnotationsForInference, because some casts
  # may become redundant and javac -Xlint:all yields "warning: [cast] redundant cast to ...".
  "$CHECKERFRAMEWORK"/checker/bin-devel/.plume-scripts/preplace -- "-Xlint:" "-Xlint:-cast," build.gradle

  echo "test-wpi-plumelib.sh for ${project} about to call wpi.sh at $(date)."
  "$CHECKERFRAMEWORK/checker/bin/wpi.sh" -b "-PskipCheckerFramework" -- --checker "$checkers"
  echo "test-wpi-plumelib.sh for ${project} returned from wpi.sh at $(date)."

  EXPECTED_FILE="$SCRIPT_DIR/$project.expected"
  DLJC_OUT_DIR="$TESTDIR/$project/dljc-out"
  ACTUAL_FILE="$DLJC_OUT_DIR"/typecheck.out
  mkdir -p "$DLJC_OUT_DIR"
  touch "${ACTUAL_FILE}"
  clean_compile_output "$EXPECTED_FILE" "expected.txt"
  clean_compile_output "$ACTUAL_FILE" "actual.txt"
  if ! cmp --quiet expected.txt actual.txt; then
    echo "Comparing $EXPECTED_FILE $ACTUAL_FILE in $(pwd)"
    diff -u expected.txt actual.txt
    if [ -n "$("$CHECKERFRAMEWORK"/checker/bin-devel/is-ci.sh)" ]; then
      # Running under continuous integration.  Output files that may be useful for debugging.
      echo "TESTDIR = ${TESTDIR}"
      echo "project = ${project}"
      echo "DLJC_OUT_DIR = ${DLJC_OUT_DIR}"
      rm -f "$DLJC_OUT_DIR"/dljc.cache
      more "$DLJC_OUT_DIR"/*
      # The string is printed by `tools/wpi.py` in the do_like_javac repository.
      AJAVADIR="$(sed -n 's/Directory for generated annotation files: \(.*\)$/\1/p' "$DLJC_OUT_DIR"/dljc-stdout-*)"
      if [ -z "$AJAVADIR" ]; then
        echo "AJAVADIR cannot be determined from $DLJC_OUT_DIR/dljc-stdout-* (printed immediately above)"
      else
        echo "AJAVADIR=$AJAVADIR"
        find "$AJAVADIR" -type f -print0 | xargs -0 more
      fi
      # Repeat the actual error, so it appears at the end of the continuous integration log.
      echo "Comparing $EXPECTED_FILE $ACTUAL_FILE in $(pwd)"
      diff -u expected.txt actual.txt
    fi
    exit 1
  fi

  cd ..
}

mkdir -p "$TESTDIR"
cd "$TESTDIR" || {
  echo "can't do: cd $TESTDIR"
  exit 2
}

# Get the list of checkers from the project's build.gradle file
## TODO: These projects are annotated for additional checkers, like resourceleak. Add to these lists.
test_wpi_plume_lib bcel-util "formatter,interning,lock,nullness,regex,signature"
test_wpi_plume_lib bibtex-clean "formatter,index,interning,lock,nullness,regex,signature"
test_wpi_plume_lib html-pretty-print "formatter,index,interning,lock,nullness,regex,signature"
test_wpi_plume_lib icalavailable "formatter,index,interning,lock,nullness,regex,signature,initializedfields"
test_wpi_plume_lib lookup "formatter,index,interning,lock,nullness,regex,signature"
## Commented out temporarily
# test_wpi_plume_lib options           "formatter,index,interning,lock,nullness,regex,signature,initializedfields"

echo "exiting test-wpi-plumelib.sh"
